/**
	main.c - Created by Timothy Morey on 3/4/2012.

	This file provides the entry point into the nvn-server application, which
	monitors a NetCDF file and sends parts of its contents to a client.
*/

#include <stdio.h>
#include <stdlib.h>

#ifdef __WINDOWS__
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <WinSock2.h>
#endif

#include <cl-args.h>
#include <data-stream.h>
#include <err.h>
#include <logger.h>
#include <messages.h>
#include <server.h>

#ifdef __WINDOWS__
#pragma warning(disable:4996)
#endif

#define SEND_BUF_SIZE 65536

int Init();
int ProcessCLArgs(int argc, char* argv[], const char** filename, int* port);
int ServerRequestReceived(Server* server, const char* clientName,
													const char* recvBuf, int recvLen,
													char* replyBuf, int* replyLen);
void Shutdown();

int main(int argc, char* argv[])
{
	const char* filename = "pism_Greenland_5km_v1.1.nc";
	int port = 12345;
	Server* controlServer = 0;
	int keepLooping = 1;
	char input[256];
	char logmsg[256];
	char sendbuf[SEND_BUF_SIZE];

  InitLogger("nvn-server.out", 
             LogLevelDebug | LogLevelInfo | LogLevelError, 
             LogLevelDebug | LogLevelInfo, 
             LogLevelError);

	if(Init() && ProcessCLArgs(argc, argv, &filename, &port))
	{
		if(NVN_NOERR == StartAsyncServer(port, ServerRequestReceived, ServerModeChatty, &controlServer))
		{
			SetSendBuffer(controlServer, sendbuf, SEND_BUF_SIZE, 0);
		}
		else
		{
			keepLooping = 0;
			WriteMessage(LogLevelError, "Failed to start server");
		}

		while(keepLooping)
		{
			printf(">>> ");
			scanf("%s", input);

			if(0 == strcmp("change-log-levels", input) ||
				 0 == strcmp("cl", input))
			{
				LogLevel file, so, se;
				scanf("%d %d %d", &file, &so, &se);
				ChangeLogLevels(file, so, se);
			}
			else if(0 == strcmp("quit", input) ||
				 0 == strcmp("q", input))
			{
				WriteMessage(LogLevelInfo, "Stopping nvn-server...");
				keepLooping = 0;
			}
			else
			{
				sprintf(logmsg, "Unrecognized command: %s", input);
				WriteMessage(LogLevelInfo, logmsg);
			}
		}

		if(controlServer)
		{
			StopServer(controlServer);
			controlServer = 0;
		}

		Shutdown();
	}

#if __WINDOWS__ && _DEBUG
	// If we're on windows, then we likely didn't launch the program from the
	// console.  This means the console will close immediately when the program
	// exits, and any messages written in the console will be lost.  Keep the 
	// console open until the user hits 'Enter' to aid in debugging.
	printf("Hit Enter to exit...\n");
	getchar();
#endif

	return 0;
}

int Init()
{
  int retval = 1;

#ifdef __WINDOWS__
  /* It is necessary to initialize Winsock once for the process. */
  if(retval)
  {
    WSADATA wsaData;
    retval = 0 == WSAStartup(MAKEWORD(2, 2), &wsaData);
  }
#endif

  return retval;
}

int ProcessCLArgs(int argc, char* argv[], const char** filename, int* port)
{
	int retval = 1;
	ArgDef args[] = {
		{ "filename", 'f', 1, "Provides the file whose data will be served up" },
    { "port",     'p', 1, "The server port" },
    { "help",     'h', 0, "Displays information about invoking this application" }
  };
	CLSchema schema = { args, 3 };
  int valid, contains;
  const char* value;

	if(NVN_NOERR == ValidateArgs(argc, argv, &schema, &valid) && valid)
	{
    if(NVN_NOERR == ContainsArg(argc, argv, "help", &schema, &contains) && contains)
    {
      PrintArgSchema(argc, argv, &schema);
      retval = 0;
    }
		
		if(NVN_NOERR == ContainsArg(argc, argv, "filename", &schema, &contains) && contains)
    {
      GetArgValue(argc, argv, "filename", &schema, &value);
			*filename = value;
    }

		if(NVN_NOERR == ContainsArg(argc, argv, "port", &schema, &contains) && contains)
    {
      GetArgValue(argc, argv, "port", &schema, &value);
			*port = atoi(value);
    }
  }
  else
  {
		PrintArgSchema(argc, argv, &schema);
    retval = 0;
  }
  
  return retval;
}

int ServerRequestReceived(Server* server, const char* clientName, 
                          const char* recvBuf, int recvLen, 
                          char* replyBuf, int* replylen)
{
  int retval = NVN_NOERR;
  MessageType msgType = MessageTypeUnknown;
  char msgName[32];
	char logmsg[1024];
  char dataset[256];
  char varname[32];

  if(NVN_NOERR == GetMessageInfo(recvBuf, recvLen, &msgType, msgName, 32))
  {
		sprintf(logmsg, "Received %s message", msgName);
		WriteMessage(LogLevelDebug, logmsg);

    if(0 == strcmp("StartStream", msgName))
    {
      int port;

      ParseStartStreamRequest(recvBuf, recvLen, &port, dataset, 256, varname, 32);
      sprintf(logmsg, "starting data stream to %s on port %d...", clientName, port);
      WriteMessage(LogLevelInfo, logmsg);
			StartDataStream(clientName, port, dataset, varname);
      CreateSimpleResponse(1, msgName, "Success", replyBuf, replylen);
    }
    else if(0 == strcmp("QueryDataGridSchema", msgName))
    {
      char dataset[256];
      char varname[32];
      DataGridSchema schema;
      
			WriteMessage(LogLevelDebug, "Handling QueryDataGridSchema");
      ParseQueryDataGridSchemaRequest(recvBuf, recvLen, dataset, 256, varname, 32);
      BuildDataGridSchema(dataset, varname, &schema);
      CreateQueryDataGridSchemaResponse(schema, replyBuf, replylen);
    }
    else if(0 == strcmp("QueryDataSetSchema", msgName))
    {
      char dataset[256];
      DataSetSchema schema;
      
			WriteMessage(LogLevelDebug, "Handling QueryDataSetSchema");
      ParseQueryDataSetSchemaRequest(recvBuf, recvLen, dataset, 256);
      BuildDataSetSchema(dataset, &schema);
      CreateQueryDataSetSchemaResponse(schema, replyBuf, replylen);
    }
  }

  return retval;
}

void Shutdown()
{
#ifdef __WINDOWS__
  /* Shutdown Winsock. */
  WSACleanup();
#endif
}
